/*
 ============================================================================
 Name        : TP2_ej2.c
 Author      : Miguel A. Salas
 Version     :
 Copyright   : Sistema Operativo
 Description : Ejercicio 2
 ============================================================================
 */

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>

const int PARENT_LEVEL_INITIAL = 0;
const int MAX_PROGENY = 2;
const int LONG_STRING_TATARA = 7;
const int LONG_MIN_STRING = 30;
const int PROGENY_LEVEL_1 = 1;
const int PROGENY_LEVEL_2 = 2;
const int PROGENY_LEVEL_3 = 3;
const int PID_INITIAL = 0;

int errorMessage(const char *msg);
void createSons(pid_t pidFirst, int gen, int total);
char* getAncestor(pid_t pidFirst, int gen);
char* getGreatGreatGrandson(int count);
char* getProgeny(pid_t pidFirst, pid_t pidSecond);

int main(int argc, char *argv[]) {
	pid_t pidFather = getpid();
	if (argc < 2)
		return errorMessage("Debe ingresar parametros");
	if (atoi(argv[1]) < 0)
		return errorMessage("Error en la cantidad de procesos a crear");

	createSons(getppid(), PARENT_LEVEL_INITIAL, atoi(argv[1]));

	if (pidFather == getpid())
		return errorMessage("Toda mi familia terminó – Fin de la ejecución");
}

void createSons(pid_t pidFirst, int gen, int total) {
	gen++;
	int count = 0;
	pid_t pidFather = getpid();
	pid_t pidSon[] = { PID_INITIAL, PID_INITIAL };
	if (gen < total) {
		for (int x = 0; x < MAX_PROGENY; x++) {
			count++;
			pid_t pid = fork();
			if (pid == -1) {
				errorMessage("Error en la creación del proceso");
				break;
			} else if (pid == 0) {
				createSons(pidFirst, gen, total);
				break;
			} else {
				pidSon[x] = pid;
			}
		}
	}

	if (pidFather == getpid())
		printf("Soy %d hijo de %d%s%s\n", pidFather, getppid(), getAncestor(pidFirst, gen), getProgeny(pidSon[0], pidSon[1]));

	for (int x = 0; x < MAX_PROGENY; x++) {
		if(pidSon[x] > PID_INITIAL)
			wait(NULL);
	}
}

char* getAncestor(pid_t pidFirst, int gen) {
	int len = LONG_MIN_STRING + LONG_STRING_TATARA * (gen - PROGENY_LEVEL_3);
	char *ancestor = (char *) malloc(sizeof(char) * len);
	if (gen > PROGENY_LEVEL_1) {
		if (gen == PROGENY_LEVEL_2)
			sprintf(ancestor, ", nieto de %d", pidFirst);
		else if (gen == PROGENY_LEVEL_3)
			sprintf(ancestor, ", bisnieto de %d", pidFirst);
		else
			sprintf(ancestor, ", %s de %d", getGreatGreatGrandson(gen - PROGENY_LEVEL_3), pidFirst);
	}
	return ancestor;
}
char* getGreatGreatGrandson(int count) {
	int len = LONG_STRING_TATARA * count;
	char *ancestor = (char *) malloc(sizeof(char) * len);
	sprintf(ancestor, "tatara");
	for (int x = 0; x < count - 1; x++)
		sprintf(ancestor, "%s-tatara", ancestor);
	sprintf(ancestor, "%snieto", ancestor);
	return ancestor;
}
char* getProgeny(pid_t pidFirst, pid_t pidSecond) {
	char *progeny = (char *) malloc(sizeof(char) * LONG_MIN_STRING);
	if (pidFirst == PID_INITIAL && pidSecond == PID_INITIAL)
		sprintf(progeny, ", sin hijos");
	else
		sprintf(progeny, ", padre de %d y %d", pidFirst, pidSecond);
	return progeny;
}

int errorMessage(const char *msg) {
	printf("%s\n", msg);
	return 1;
}
